{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## **演示0102：数组维度**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### **案例1：数组维度和长度**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> **与数组维度和大小有关的函数和属性**\n",
    ">* *len*函数总是返回数组第一个维度的大小\n",
    ">* *shape*属性返回数组每个维度的大小，使用一个*tuple*来代表；对于一维数组，*shape*属性仅记录了一个维度大小\n",
    ">* *size*属性用于记录元素总个数\n",
    ">* *itemsize*属性记录了每个元素所占字节数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "10 (10,) 10 4\n",
      "2 (2, 4) 8 8\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "a1 = np.arange(10)\n",
    "print(len(a1), a1.shape, a1.size, a1.itemsize)\n",
    "a2 = np.array([[1,2,3,4],[5,6,7,8]], dtype=np.int64)\n",
    "print(len(a2), a2.shape, a2.size, a2.itemsize)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### **案例2：访问数组中的元素**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> **使用正数下标索引访问一维数组中的元素**  \n",
    "数组元素：[0 10 20 30 40 50 60 70 80 90]  \n",
    "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n",
    "$\\downarrow$ &nbsp; $\\downarrow$ &nbsp;$\\downarrow$ &nbsp; $\\downarrow$ &nbsp;$\\downarrow$ &nbsp; $\\downarrow$ &nbsp;$\\downarrow$ &nbsp; $\\downarrow$ &nbsp;$\\downarrow$ &nbsp; $\\downarrow$  \n",
    "下标索引： 0 &nbsp; 1 &nbsp; 2 &nbsp; 3 &nbsp; 4 &nbsp; 5 &nbsp; 6 &nbsp; 7 &nbsp; 8 &nbsp; 9 &nbsp;  \n",
    ">* 下标索引从$0$开始编号\n",
    ">* 使用 *[start:end:step]* 的形式从数组中取出部分元素(切片)：从下标为 *start* 的元素开始，到下标为 *end-1* 的元素结束，每次移动 *step* 个元素\n",
    ">* 如果不明确指定 *start* , *end* 和 *step*，则$start=0$, $end=len(a)$, $step=1$  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ 0 10 20 30 40 50 60 70 80 90]\n",
      "[ 0 10 20 30 40 50 60 70 80 90]\n",
      "[ 0 10 20 30 40 50 60 70 80 90]\n",
      "[ 0 10 20 30 40 50 60 70 80 90]\n",
      "[10 20 30 40 50 60 70 80 90]\n",
      "[10 30 50 70 90]\n",
      "[10 30 50 70 90]\n",
      "[ 0 20 40 60 80]\n"
     ]
    }
   ],
   "source": [
    "a1 = np.arange(10)*10\n",
    "print(a1)\n",
    "print(a1[0:len(a1)])    # start=0，end=10。从下标为0的元素开始，到下标为9(end-1=9)的元素结束\n",
    "print(a1[0:])    # start=0，end=10\n",
    "print(a1[:])     # start=0, end=10\n",
    "print(a1[1:])    # start=1, end=10\n",
    "print(a1[1:len(a1):2])    # start=1, end=10, step=2\n",
    "print(a1[1::2])    # start=1, end=10, step=2\n",
    "print(a1[::2])    # start=0, end=10, step=2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> **使用负数下标索引访问一维数组中的元素**  \n",
    "数组元素：[&nbsp;&nbsp;&nbsp;0&nbsp;&nbsp;&nbsp;10&nbsp;&nbsp;&nbsp;20&nbsp;&nbsp;30&nbsp;&nbsp;40&nbsp;&nbsp;50&nbsp;&nbsp;60&nbsp;&nbsp;70&nbsp;&nbsp;80&nbsp;&nbsp;90]  \n",
    "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\n",
    "$\\downarrow$ &nbsp;&nbsp; $\\downarrow$ &nbsp;&nbsp;&nbsp;$\\downarrow$ &nbsp;&nbsp; $\\downarrow$ &nbsp;&nbsp;$\\downarrow$ &nbsp;&nbsp; $\\downarrow$ &nbsp;&nbsp;$\\downarrow$ &nbsp;&nbsp; $\\downarrow$ &nbsp;&nbsp;&nbsp;$\\downarrow$ &nbsp;&nbsp; $\\downarrow$  \n",
    "下标索引： -10&nbsp;&nbsp; -9&nbsp;&nbsp; -8&nbsp;&nbsp; -7&nbsp;&nbsp; -6&nbsp;&nbsp; -5&nbsp;&nbsp; -4&nbsp;&nbsp; -3&nbsp;&nbsp; -2&nbsp;&nbsp; -1 \n",
    ">* 最后一个元素的下标索引为$-1$,依次向前(左)减少 \n",
    ">* 使用 *[start:end:step]* 形式取出数组中的某个部分(切片)时。默认情况下，$step=1$\n",
    ">* 当$step\\gt0$，默认$start=-(len(a))$， $end=0$, 从下标为 *start* 的元素开始取，到下标为 *end-1* 的元素结束\n",
    ">* 当$step\\lt0$(例如$step=-1$)，则默认$start=-1$, $end=-(len(a)+1)$。从下标为 *start* 的元素开始，向前(左)步进(每次 *step* 步)，一直到下标为 *end+1* 的元素结束"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ 0 10 20 30 40 50 60 70 80 90]\n",
      "90\n",
      "[ 0 10 20 30 40 50 60 70 80]\n",
      "[60 70 80 90]\n",
      "[60 70 80]\n",
      "[60 50 40 30]\n",
      "[80 70 60 50 40 30]\n",
      "[90 80 70 60 50 40 30 20 10  0]\n"
     ]
    }
   ],
   "source": [
    "a1 = np.arange(10)*10\n",
    "print(a1)\n",
    "print(a1[-1])    # 下标为-1的元素(最后一个元素)\n",
    "print(a1[:-1])    # start=-len(a), end=-1。从第一个元素开始，到下标为-2(end-1=-2)的元素结束\n",
    "print(a1[-4:])    # start=-4，end=0。从下标为-4的元素开始，到下标为-1(end-1=-1)的元素结束\n",
    "print(a1[-4:-1])    # start=-4，end=-1。从下标为-4的元素开始，到下标为-2(end-1=-2)的元素结束\n",
    "print(a1[6:2:-1])    # start=6, end=2, step=-1。从正下标为6的元素开始，到正下标为3(end+1=3)的元素结束，向前(左)依次取元素\n",
    "print(a1[-2:2:-1])    # start=-2, end=2, step=-1。从负下标为-2的元素开始，到正下标为3(end+1=3)的元素结束，向前(左)依次取元素\n",
    "print(a1[::-1])    # start=-1，end=-(10+1)=-11。从负下标为-1元素开始，到负下标为-10(end+1=-10)的元素结束，向前(左)依次取元素"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> **使用下标索引访问二维数组/矩阵中的单个元素**  \n",
    ">* 分为*row*和*col*两个维度索引，分别表示行下标和列下标。行、列的下标均从$0$开始。\n",
    ">* *[row][col]* 或 *[row, col]* 形式的效果相同"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "8\n",
      "8\n"
     ]
    }
   ],
   "source": [
    "a= np.array([[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]])\n",
    "print(a[1,2])    # 行下标为1，列下标为1的元素\n",
    "print(a[1][2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> **访问二维数组/矩阵中的部分数据(切片)**  \n",
    ">* 行、列下标可以采用与一维数组类似的方式进行处理\n",
    ">* 如果最终的切片结果只有一行或一列，则该结果将被转成一个一维数组"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 3  4  5]\n",
      " [ 8  9 10]]\n",
      "[[ 3  4  5]\n",
      " [ 8  9 10]\n",
      " [13 14 15]]\n",
      "[ 4  9 14]\n",
      "[ 6  7  8  9 10]\n",
      "[ 6  7  8  9 10]\n"
     ]
    }
   ],
   "source": [
    "a= np.array([[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15]])\n",
    "print(a[0:2,2:5])    # 行下标0，1行，列下标2,3,4列\n",
    "print(a[:, 2:5])    # 行下标所有行，列下标2,3,4列\n",
    "print(a[:,3])    # 行下标所有行，列下标为3的列。注意，因为最终只返回了1列，因此该列数据被转换成一维数组\n",
    "print(a[1,:])    # 行下标为1的行，列下标所有列。注意，因为最终只返回了1行，因此改行数据被转换成一维数组\n",
    "print(a[1])    # 如果省略了列维度，则默认取所有列"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> **通过 *[start:end:step]* 方式得到的切片数据，与原数组共享内存地址**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[100   2   3   4   5]\n"
     ]
    }
   ],
   "source": [
    "a = np.array([1,2,3,4,5])\n",
    "b = a[0:2]\n",
    "b[0] = 100\n",
    "print(a)    # a的第一个元素被修改为100"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### **案例3：变更数组的维度**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> **将维度为(3,4)的数组转变成(4,3)数组形式**  \n",
    ">* *reshape*方法：\n",
    ">  * 必须保证变形前后的总元素个数相同\n",
    ">  * 变形前后不会产生新的拷贝数据。即：变形前后两个变量共享同一块数据内存\n",
    ">  * 虽然两个变量共享相同的数据内存，但是它们各自维护自己的维度\n",
    ">* *np.resize*方法：\n",
    ">  * 变形后的总元素个数可以不同于变形前的\n",
    ">  * *np.resize(a,(M, N))*，不会修改a的维度，将返回一个$(M, N)$的数组拷贝\n",
    ">  * *np.resize(a, (M,N))*与*a.resize((M,N))*的行为并不相同，本实验不作演示"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 1  2  3]\n",
      " [ 4  5  6]\n",
      " [ 7  8  9]\n",
      " [10 11 12]]\n",
      "100\n",
      "a1.shape= (3, 4)\n",
      "a2.shape= (4, 3)\n"
     ]
    }
   ],
   "source": [
    "a1 = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])\n",
    "a2 = a1.reshape((4, 3))\n",
    "# a2 = np.reshape(a1, (4, 3))    # 与上一行相同效果\n",
    "print(a2)\n",
    "a1[0, 0] = 100\n",
    "print(a2[0, 0])    # 与a1共享数据内存\n",
    "print(\"a1.shape=\", a1.shape)\n",
    "print(\"a2.shape=\", a2.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[ 1  2  3]\n",
      " [ 4  5  6]\n",
      " [ 7  8  9]\n",
      " [10 11 12]]\n",
      "1\n"
     ]
    }
   ],
   "source": [
    "a1 = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])\n",
    "a2 = np.resize(a1, (4, 3))\n",
    "print(a2)\n",
    "a1[0, 0] = 100\n",
    "print(a2[0, 0])    # 各自拥有独立的数据内存"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[1 2 3]\n",
      " [4 5 6]]\n",
      "[[ 1  2  3]\n",
      " [ 4  5  6]\n",
      " [ 7  8  9]\n",
      " [10 11 12]\n",
      " [ 1  2  3]]\n"
     ]
    }
   ],
   "source": [
    "a1 = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12]])\n",
    "a2 = np.resize(a1, (2, 3))    # 仅取前6个元素转成2x3数组\n",
    "print(a2)\n",
    "a3 = np.resize(a1, (5, 3))    # 变形后的元素个数更多，这时将反复填充原始数组中的数据\n",
    "print(a3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "> **行向量与列向量**  \n",
    ">* 形如[1 2 3 4 5]的向量，称作**行向量**\n",
    ">  * 行向量实际上可视为一维数组\n",
    ">  * 可通过：<code>np.array([1,2,3,4,5])</code>定义\n",
    ">  * *shape*表示为(5, )\n",
    ">  * 一般不把形如 [[1 2 3 4 5]]的$(1, N)$维度矩阵视为行向量，虽然它代表了矩阵中的一行。shape表示为(1, 5)\n",
    ">* 形如$ \\left[\\begin{matrix}1 \\\\ 2 \\\\ 3 \\\\ 4 \\\\ 5\\end{matrix}\\right] $的向量，称作**列向量**\n",
    ">  * 列向量实际上是一个$(N, 1)$维度的矩阵\n",
    ">  * 可通过：<code>np.array([[1],[2],[3],[4],[5]])</code>定义\n",
    ">  * *shape*表示为(5, 1)\n",
    ">* 行向量转成列向量\n",
    ">  * *newaxis*操作符。操作前后两个变量引用同一块数据内存，但是维护各自的shape信息\n",
    ">  * *reshape*。可以使用-1让函数自动推算某个维度的长度\n",
    ">  * *resize*。需要明确指出维度长度，不能通过-1让函数自动推算\n",
    ">* 列向量转成(1,N)维度矩阵\n",
    ">  * *reshape*或*resize*\n",
    ">* 列向量转成(N，)维度的行向量(一维数组)\n",
    ">  * *ravel*：转换前后共享数据内存\n",
    ">  * *flatten*：转换后拷贝一份内存。注意应通过对象来调用*flatten*，而不能使用*np.flatten*\n",
    ">  * $(M,N)$维度的二维数组/矩阵，同样可以通过*ravel*或*flatten*展成一维数组"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[1]\n",
      " [2]\n",
      " [3]\n",
      " [4]\n",
      " [5]]\n",
      "[[1]\n",
      " [2]\n",
      " [3]\n",
      " [4]\n",
      " [5]]\n",
      "[[1]\n",
      " [2]\n",
      " [3]\n",
      " [4]\n",
      " [5]]\n"
     ]
    }
   ],
   "source": [
    "# 行向量转列向量\n",
    "a = np.array([1,2,3,4,5])\n",
    "b = a[:, np.newaxis]\n",
    "c = np.reshape(a, (-1, 1))    # -1表示行数维度由函数自行推算\n",
    "d = np.resize(a, (len(a), 1))\n",
    "print(b)\n",
    "print(c)\n",
    "print(d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[1 2 3 4 5]]\n"
     ]
    }
   ],
   "source": [
    "# 列向量转(1,N)维度矩阵\n",
    "a = np.array([[1],[2],[3],[4],[5]])\n",
    "b = a.reshape((1, -1))\n",
    "print(b)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1 2 3 4 5]\n",
      "[1 2 3 4 5]\n"
     ]
    }
   ],
   "source": [
    "# 列向量转(N,)行向量(一维数组)\n",
    "a = np.array([[1],[2],[3],[4],[5]])\n",
    "b = np.ravel(a)\n",
    "c = a.flatten()\n",
    "print(b)\n",
    "print(c)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
